//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "DistanceBindingSite.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <math.h>

#include <iostream>

#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace residuesBinding {

  bool PositionRes(TNode <String> *NodeT, void* ref) {

    String* resA = NodeT->Info();
    String* resB = ((TNode <String>*)ref)->Info();

    char posA[resA->Length() + 1];
    char posB[resB->Length() + 1];

    strcpy(posA, resA->Out());
    strcpy(posB, resB->Out());

    char* part1 = strtok(posA, "_ ");
    part1 = strtok(NULL, "_ ");

    char* part2 = strtok(posB, "_ ");
    part2 = strtok(NULL, "_ ");

    bool result = (atoi(part1) < atoi(part2))?true:false;

    return result;

  }

  bool EqualResPos(TNode <String> *NodeT, void* ref) {
    String* seqA   = NodeT->Info();
    String* seqB   = ((TNode <String>*)ref)->Info();
    seqA->RTrim(); seqB->RTrim();
    return (!strcmp(seqA->Out(), seqB->Out()))?true:false;
  }

};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
DistanceBindingSite::DistanceBindingSite() {
    
  this->idDB                 = new String();
  this->location             = new String();
  this->outFile              = new String();
  
  this->complementaryPDBInfo = new String();
  this->proteinSeq           = new String();

  this->ligandFormul         = new TListE <String> ();
  this->listLigands          = new TListE <UniqueLigand> ();

  this->chain                = new String();
  this->identFasta           = new String();
  this->listResidues         = new String();

  this->listATOM             = new TListE <String> ();
  this->listHETATM           = new TListE <String> ();

  this->xLower               = 0;
  this->xHigher              = 0;
  this->yLower               = 0;
  this->yHigher              = 0;
  this->zLower               = 0;
  this->zHigher              = 0;

  this->distance             = 0;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
DistanceBindingSite::DistanceBindingSite(const DistanceBindingSite& distanceBindingSite) {

  this->idDB                 = new String(distanceBindingSite.idDB);
  this->location             = new String(distanceBindingSite.location);
  this->outFile              = new String(distanceBindingSite.outFile);
  
  this->complementaryPDBInfo = new String(distanceBindingSite.complementaryPDBInfo);
  this->proteinSeq           = new String(distanceBindingSite.proteinSeq);

  this->ligandFormul         = new TListE <String> (distanceBindingSite.ligandFormul);
  this->listLigands          = new TListE <UniqueLigand> (distanceBindingSite.listLigands);

  this->chain                = new String(distanceBindingSite.chain);
  this->identFasta           = new String(distanceBindingSite.identFasta);
  this->listResidues         = new String(distanceBindingSite.listResidues);

  this->listATOM             = new TListE <String> (distanceBindingSite.listATOM);
  this->listHETATM           = new TListE <String> (distanceBindingSite.listHETATM);

  this->xLower               = distanceBindingSite.xLower;
  this->xHigher              = distanceBindingSite.xHigher;
  this->yLower               = distanceBindingSite.yLower;
  this->yHigher              = distanceBindingSite.yHigher;
  this->zLower               = distanceBindingSite.zLower;
  this->zHigher              = distanceBindingSite.zHigher;

  this->distance             = distanceBindingSite.distance;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
DistanceBindingSite::DistanceBindingSite(const DistanceBindingSite* distanceBindingSite) {

  this->idDB                 = new String(distanceBindingSite->idDB);
  this->location             = new String(distanceBindingSite->location);
  this->outFile              = new String(distanceBindingSite->outFile);

  this->complementaryPDBInfo = new String(distanceBindingSite->complementaryPDBInfo);
  this->proteinSeq           = new String(distanceBindingSite->proteinSeq);

  this->ligandFormul         = new TListE <String> (distanceBindingSite->ligandFormul);
  this->listLigands          = new TListE <UniqueLigand> (distanceBindingSite->listLigands);

  this->chain                = new String(distanceBindingSite->chain);
  this->identFasta           = new String(distanceBindingSite->identFasta);
  this->listResidues         = new String(distanceBindingSite->listResidues);

  this->listATOM             = new TListE <String> (distanceBindingSite->listATOM);
  this->listHETATM           = new TListE <String> (distanceBindingSite->listHETATM);

  this->xLower               = distanceBindingSite->xLower;
  this->xHigher              = distanceBindingSite->xHigher;
  this->yLower               = distanceBindingSite->yLower;
  this->yHigher              = distanceBindingSite->yHigher;
  this->zLower               = distanceBindingSite->zLower;
  this->zHigher              = distanceBindingSite->zHigher;

  this->distance             = distanceBindingSite->distance;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
DistanceBindingSite::~DistanceBindingSite() {

  if (this->idDB)                  delete this->idDB;
  if (this->location)              delete this->location;
  if (this->outFile)               delete this->outFile;

  if (this->complementaryPDBInfo)  delete this->complementaryPDBInfo;
  if (this->proteinSeq)            delete this->proteinSeq;

  if (this->ligandFormul)          delete this->ligandFormul;
  if (this->listLigands)           delete this->listLigands;

  if (this->chain)                 delete this->chain;
  if (this->identFasta)            delete this->identFasta;
  if (this->listResidues)          delete this->listResidues;
  
  if (this->listATOM)              delete this->listATOM;
  if (this->listHETATM)            delete this->listHETATM;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::IDPDB(String* idDB) {
  if (idDB) {
    if (this->idDB) delete this->idDB;
    this->idDB = idDB;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::Location(String* location) {
  if (location) {
    if (this->location) delete this->location;
    this->location = location;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::OutFile(String* outFile) {
  if (outFile) {
    if (this->outFile) delete this->outFile;
    this->outFile = outFile;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ComplementaryPDBInfo(String* complementaryPDBInfo) {
  if (complementaryPDBInfo) {
    if (this->complementaryPDBInfo) delete this->complementaryPDBInfo;
    this->complementaryPDBInfo = complementaryPDBInfo;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ProteinSeq(String* proteinSeq) {
  if (proteinSeq) {
    if (this->proteinSeq) delete this->proteinSeq;
    this->proteinSeq = proteinSeq;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::LigandFormul(TListE <String>* ligandFormul) {
  if (ligandFormul) {
    if (this->ligandFormul) delete this->ligandFormul;
    this->ligandFormul = ligandFormul;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ListLigands(TListE <UniqueLigand>* listLigands) {
  if (listLigands) {
    if (this->listLigands) delete this->listLigands;
    this->listLigands = listLigands;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::Chain(String* chain) {
  if (chain) {
    if (this->chain) delete this->chain;
    this->chain = chain;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::IdentFasta(String* identFasta) {
  if (identFasta) {
    if (this->identFasta) delete this->identFasta;
    this->identFasta = identFasta;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ListResidues(String* listResidues) {
  if (listResidues) {
    if (this->listResidues) delete this->listResidues;
    this->listResidues = listResidues;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ListATOM(TListE <String>* listATOM) {
  if (listATOM) {
    if (this->listATOM) delete this->listATOM;
    this->listATOM = listATOM;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::ListHETATM(TListE <String>* listHETATM) {
  if (listHETATM) {
    if (this->listHETATM) delete this->listHETATM;
    this->listHETATM = listHETATM;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void DistanceBindingSite::Distance(double distance) {
  this->distance = distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::IDPDB(void) {
  return this->idDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::Location(void) {
  return this->location;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::OutFile(void) {
  return this->outFile;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::ComplementaryPDBInfo(void) {
  return this->complementaryPDBInfo;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::ProteinSeq(void) {
  return this->proteinSeq;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* DistanceBindingSite::LigandFormul(void) {
  return this->ligandFormul;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <UniqueLigand>* DistanceBindingSite::ListLigands(void) {
  return this->listLigands;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::Chain(void) {
  return this->chain;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::IdentFasta(void) {
  return this->identFasta;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* DistanceBindingSite::ListResidues(void) {
  return this->listResidues;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* DistanceBindingSite::ListATOM(void) {
  return this->listATOM;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::XLower(void) {
  return this->xLower;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::XHigher(void) {
  return this->xHigher;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::YLower(void) {
  return this->yLower;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::YHigher(void) {
  return this->yHigher;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::ZLower(void) {
  return this->zLower;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::ZHigher(void) {
  return this->zHigher;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::Distance(void) {
  return this->distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::ConstructPDBBindingSite(void) {

  if (!this->GetLimitOfDistanceBindingSite()) return false;

  this->listATOM->Clear();

  this->identFasta->Add(">");
  this->identFasta->Add(this->idDB);
  this->identFasta->Add("_");
  this->identFasta->Add(this->chain);

  gzFile           pdbFile = gzopen(this->location->Out(), "rb9");
  char             buffer[LENGTHLINE + 1];

  while (!gzeof(pdbFile)) {
    gzgets(pdbFile, buffer, LENGTHLINE);
    if (!strncmp(buffer, "ATOM", strlen("ATOM")) && (buffer[21] == (this->chain->Out())[0])) {
      String* line = new String(buffer);
      this->listATOM->Add(line);
    }
  }

  gzclose(pdbFile);

  TListE <String>* listResiduesPos = new TListE <String> ();

  cout << this->listHETATM->Length() << endl;

  this->listHETATM->SetInitial();

  while (this->listHETATM->SetNext()) {

    String* xValue = this->listHETATM->GetCurrent()->SubStr(30, 38);
    String* yValue = this->listHETATM->GetCurrent()->SubStr(38, 46);
    String* zValue = this->listHETATM->GetCurrent()->SubStr(46, 54);

    double xRef = atof(xValue->Out());
    double yRef = atof(yValue->Out());
    double zRef = atof(zValue->Out());

    char*  x        = NULL;
    char*  y        = NULL;
    char*  z        = NULL;

    this->listATOM->SetInitial();

    while (this->listATOM->SetNext()) {

      char* position = NULL;
      char* temporal = NULL;

      position = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 23, 26, position);
      temporal = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 17, 20, temporal);

      String* tmpPosition = new String(position);
      String* tmpString   = new String(temporal);

      if (position) delete[] position;
      if (temporal) delete[] temporal;

      tmpPosition->RTrim();
      tmpString->RTrim();

      char residue = this->OneLetterAACode(tmpString);

      if (residue == ' ') {
        if (tmpPosition) delete tmpPosition;
        if (tmpString)   delete tmpString;
        continue;
      }

      x = y = z = NULL;

      x = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 31, 38, x);
      y = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 39, 46, y);
      z = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 47, 54, z);

      double realDistance = 0;

      if ((realDistance = this->DistancePoints(atof(x), atof(y), atof(z), xRef, yRef, zRef)) <= this->distance) {

        String* record = new String();

        record->Add(residue);
        record->Add("_");
        record->Add(tmpPosition->Out());
        record->RTrim();

        listResiduesPos->Add(record);

      }

      if (x) delete x;
      if (y) delete y;
      if (z) delete z;

      if (tmpPosition) delete tmpPosition;
      if (tmpString)   delete tmpString;

    }

    if (xValue) delete xValue;
    if (yValue) delete yValue;
    if (zValue) delete zValue;

  }

  listResiduesPos->QuickSortList(residuesBinding::PositionRes, NULL);
  listResiduesPos->CompactListAfterOrdered(residuesBinding::EqualResPos, NULL);

  listResiduesPos->SetInitial();

  int counter = 0;

  while (listResiduesPos->SetNext()) {
    if (counter++) this->listResidues->Add(",");
    this->listResidues->Add(listResiduesPos->GetCurrent()->Out());
  }

  if (listResiduesPos) delete listResiduesPos;

  if (!this->listResidues->Length()) this->listResidues->Add(",");

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::ConstructPDBBindingSite(UniqueLigand* uniqueLigand) {

  if (!this->GetLimitOfDistanceBindingSite(uniqueLigand)) return false;

  this->listATOM->Clear();

  this->identFasta->Add(">");
  this->identFasta->Add(this->idDB);
  this->identFasta->Add("_");
  this->identFasta->Add(this->chain);

  gzFile           pdbFile = gzopen(this->location->Out(), "rb9");
  char             buffer[LENGTHLINE + 1];

  while (!gzeof(pdbFile)) {
    gzgets(pdbFile, buffer, LENGTHLINE);
    if (!strncmp(buffer, "ATOM", strlen("ATOM")) && (buffer[21] == (this->chain->Out())[0])) {
      String* line = new String(buffer);
      this->listATOM->Add(line);
    }
  }

  gzclose(pdbFile);

  TListE <String>* listResiduesPos = new TListE <String> ();

  this->listHETATM->SetInitial();

  while (this->listHETATM->SetNext()) {

    String* xValue = this->listHETATM->GetCurrent()->SubStr(30, 38);
    String* yValue = this->listHETATM->GetCurrent()->SubStr(38, 46);
    String* zValue = this->listHETATM->GetCurrent()->SubStr(46, 54);

    double xRef = atof(xValue->Out());
    double yRef = atof(yValue->Out());
    double zRef = atof(zValue->Out());

    char*  x        = NULL;
    char*  y        = NULL;
    char*  z        = NULL;

    this->listATOM->SetInitial();

    while (this->listATOM->SetNext()) {

      char* position = NULL;
      char* temporal = NULL;

      position = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 23, 26, position);
      temporal = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 17, 20, temporal);

      String* tmpPosition = new String(position);
      String* tmpString   = new String(temporal);

      if (position) delete[] position;
      if (temporal) delete[] temporal;

      tmpPosition->RTrim();
      tmpString->RTrim();

      char residue = this->OneLetterAACode(tmpString);

      if (residue == ' ') {
        if (tmpPosition) delete tmpPosition;
        if (tmpString)   delete tmpString;
        continue;
      }

      x = y = z = NULL;

      x = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 31, 38, x);
      y = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 39, 46, y);
      z = this->SubStrFromLine(this->listATOM->GetCurrent()->Out(), 47, 54, z);

      double realDistance = 0;

      if ((realDistance = this->DistancePoints(atof(x), atof(y), atof(z), xRef, yRef, zRef)) <= this->distance) {

        String* record = new String();

        record->Add(residue);
        record->Add("_");
        record->Add(tmpPosition->Out());
        record->RTrim();
        
        listResiduesPos->Add(record);
        
      }

      if (x) delete x;
      if (y) delete y;
      if (z) delete z;

      if (tmpPosition) delete tmpPosition;
      if (tmpString)   delete tmpString;

    }

    if (xValue) delete xValue;
    if (yValue) delete yValue;
    if (zValue) delete zValue;

  }

  listResiduesPos->QuickSortList(residuesBinding::PositionRes, NULL);
  listResiduesPos->CompactListAfterOrdered(residuesBinding::EqualResPos, NULL);

  listResiduesPos->SetInitial();

  int counter = 0;

  while (listResiduesPos->SetNext()) {
    if (counter++) this->listResidues->Add(",");
    this->listResidues->Add(listResiduesPos->GetCurrent()->Out());
  }

  if (listResiduesPos) delete listResiduesPos;

  if (!this->listResidues->Length()) this->listResidues->Add(",");

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::GetLimitOfDistanceBindingSite(void) {
    
  if (!this->location || !this->location->Length()) return false;

  gzFile           pdbFile     = gzopen(this->location->Out(), "rb9");

  char             buffer[LENGTHLINE + 1];
  bool             condAnalyze = false;

  String*          textHETATM  = new String();
  
  TListE <String>* lines       = new TListE <String> ();

  while (!gzeof(pdbFile)) {

    gzgets(pdbFile, buffer, LENGTHLINE);

    if (!strncmp(buffer, "MODEL", strlen("MODEL")))
      textHETATM->In("");
    
    if (condAnalyze) {

      if (!strncmp(buffer, "ATOM", strlen("ATOM"))) {
        condAnalyze = false;
        continue;
      }

      if (!strncmp(buffer, "HETATM", strlen("HETATM"))) {


        String* tmpBuffer = new String(buffer);
        String* atomType  = tmpBuffer->SubStr(12, 16);
        String* resName   = tmpBuffer->SubStr(17, 20);
        String* elemSymb  = tmpBuffer->SubStr(76, 78);

        atomType->RTrim();
        resName->RTrim();
        elemSymb->RTrim();

        if (tmpBuffer) delete tmpBuffer;

        if (strcmp(atomType->Out(), resName->Out()) && 
            strcmp("HOH", resName->Out())           &&
            strcmp("H",   elemSymb->Out()))
          textHETATM->Add(buffer);

        if (atomType) delete atomType;
        if (resName)  delete resName;
        if (elemSymb) delete elemSymb;
        
      }
    }

    if (!strncmp(buffer, "TER", strlen("TER")))
      condAnalyze = true;

  }

  gzclose(pdbFile);

  this->SplitColumn(textHETATM->Out(), lines, "\n");

  this->chain->In("");

  while (lines->Get(0)) {
    String* ChainV = lines->GetCurrent()->SubStr(21, 22);
    if (!this->chain->Length()) 
      this->chain->In(ChainV);
    if (!strcmp(this->chain->Out(), ChainV->Out())) 
      this->listHETATM->Add(lines->TransferCurrent());
    else break;
  }

  if (lines)      delete lines;
  if (textHETATM) delete textHETATM;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::GetLimitOfDistanceBindingSite(UniqueLigand* uniqueLigand) {

  if (!this->location || !this->location->Length() || !uniqueLigand) return false;

  gzFile           pdbFile     = gzopen(this->location->Out(), "rb9");
  String*          textHETATM  = new String();
  TListE <String>* lines       = new TListE <String> ();

  char             buffer[LENGTHLINE + 1];

  uniqueLigand->IDELigand->RTrim();
  uniqueLigand->ResidueNumber->RTrim();
  
  while (!gzeof(pdbFile)) {

    gzgets(pdbFile, buffer, LENGTHLINE);

    if (!strncmp(buffer, "MODEL", strlen("MODEL")))
      textHETATM->In("");

    if (!strncmp(buffer, "HETATM", strlen("HETATM"))) {

      String* tmpBuffer = new String(buffer);
      String* resNumber = tmpBuffer->SubStr(22, 26);
      String* resName   = tmpBuffer->SubStr(17, 20);

      resNumber->RTrim();
      resName->RTrim();

      if (!strcmp(uniqueLigand->IDELigand->Out(),     resName->Out())    &&
          !strcmp(uniqueLigand->ResidueNumber->Out(), resNumber->Out())) 
        textHETATM->Add(buffer);

      if (resNumber) delete resNumber;
      if (resName)   delete resName;
      if (tmpBuffer) delete tmpBuffer;

    }

  }

  gzclose(pdbFile);

  this->SplitColumn(textHETATM->Out(), lines, "\n");
  this->chain->In("");

  while (lines->Get(0)) {
    String* ChainV = lines->GetCurrent()->SubStr(21, 22);
    if (!this->chain->Length())
      this->chain->In(ChainV);
    if (!strcmp(this->chain->Out(), ChainV->Out())) 
      this->listHETATM->Add(lines->TransferCurrent());
    else break;
  }

  if (lines)      delete lines;
  if (textHETATM) delete textHETATM;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::IsCoordinateInsideBox(double x, double y, double z) {
  if ((x >= this->xLower) && (x <= this->xHigher) &&
      (y >= this->yLower) && (y <= this->yHigher) &&
      (z >= this->zLower) && (z <= this->zHigher))
    return true;
  return false;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::IsCoordinateInsideBox(double x, double y, double z, double distance) {
  if ((x >= (this->xLower - distance)) && (x <= (this->xHigher + distance)) &&
      (y >= (this->yLower - distance)) && (y <= (this->yHigher + distance)) &&
      (z >= (this->zLower - distance)) && (z <= (this->zHigher + distance)))
    return true;
  return false;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* DistanceBindingSite::SubStrFromLine(char* line, int initPos, int finalPos, char* temporal) {

  if ((!line) || (initPos < 0) || (finalPos >= strlen(line))) return NULL;

  if (temporal) delete[] temporal;

  temporal = new char[finalPos - initPos + 1];
  int   i, j;
  for (i = initPos, j = 0; i < finalPos; i++) 
    temporal[j++] = line[i];
  temporal[j] = '\0';
  
  return temporal;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char DistanceBindingSite::OneLetterAACode(String* code) {

  if      (!code)                return ' ';
  if      (code->Contain("ALA")) return 'A';
  else if (code->Contain("ARG")) return 'R';
  else if (code->Contain("ASN")) return 'N';
  else if (code->Contain("ASP")) return 'D';
  else if (code->Contain("ASX")) return 'B';
  else if (code->Contain("CYS")) return 'C';
  else if (code->Contain("GLU")) return 'E';
  else if (code->Contain("GLN")) return 'Q';
  else if (code->Contain("GLX")) return 'Z';
  else if (code->Contain("GLY")) return 'G';
  else if (code->Contain("HIS")) return 'H';
  else if (code->Contain("ILE")) return 'I';
  else if (code->Contain("LEU")) return 'L';
  else if (code->Contain("LYS")) return 'K';
  else if (code->Contain("MET")) return 'M';
  else if (code->Contain("PHE")) return 'F';
  else if (code->Contain("PRO")) return 'P';
  else if (code->Contain("SER")) return 'S';
  else if (code->Contain("THR")) return 'T';
  else if (code->Contain("TRP")) return 'W';
  else if (code->Contain("TYR")) return 'Y';
  else if (code->Contain("VAL")) return 'V';
  else                           return ' ';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool DistanceBindingSite::GetListLigandInComplex() {

  if (!this->location || !this->location->Length()) return false;

  TListE <String>* lines     = new TListE <String> ();
  TListE <int>*    listResN  = new TListE <int>    ();

  String*          tmpChain = new String();
  String*          tmpResN  = new String();

  bool             resNChange = false;

  char    buffer[LENGTHLINE + 1];

  gzFile           pdbFile  = gzopen(this->location->Out(), "rb9");

  while (!gzeof(pdbFile)) {

    gzgets(pdbFile, buffer, LENGTHLINE);

    if (!strncmp(buffer, "MODEL", strlen("MODEL"))) {
      lines->Clear();
      listResN->Clear();
      tmpChain->In("");
    }

    if (!strncmp(buffer, "HETATM", strlen("HETATM"))) {

      String* tmpBuffer = new String(buffer);
      String* atomType  = tmpBuffer->SubStr(12, 16);
      String* resName   = tmpBuffer->SubStr(17, 20);
      String* chain     = tmpBuffer->SubStr(21, 22);
      String* resNumber = tmpBuffer->SubStr(22, 26);

      atomType->RTrim(); resName->RTrim();

      if (!tmpChain->Length()) tmpChain->In(chain->Out());

      if (!tmpResN->Length() || strcmp(tmpResN->Out(), resNumber->Out())) {
        tmpResN->In(resNumber->Out());
        resNChange = true;
      }

      if (strcmp(atomType->Out(), resName->Out()) &&
          strcmp("HOH", resName->Out())           &&
          !strcmp(tmpChain->Out(), chain->Out())) {

        String* tmpHETATM = new String(buffer);
        lines->Add(tmpHETATM);
        int* change = new int;
        *change     = (resNChange)?1:0;
        resNChange  = false;
        listResN->Add(change);
      }

      if (atomType)  delete atomType;
      if (resName)   delete resName;
      if (tmpBuffer) delete tmpBuffer;
      if (chain)     delete chain;
      if (resNumber) delete resNumber;

    }
  }

  gzclose(pdbFile);

  if (tmpChain) delete tmpChain;
  if (tmpResN)  delete tmpResN;

  for (int i = listResN->Length() - 1, cumulCount = 0; i >= 0 ; i--, cumulCount++)
    if (*(listResN->Get(i)) == 1) {
      if (cumulCount >= 9) {

        UniqueLigand* uniqueLigand = new UniqueLigand();

        String* resName   = lines->Get(i)->SubStr(17, 20);
        String* resNumber = lines->Get(i)->SubStr(22, 26);

        uniqueLigand->IDELigand->In(resName->Out());
        uniqueLigand->ResidueNumber->In(resNumber->Out());
        uniqueLigand->amountAtoms = cumulCount + 1;

        this->listLigands->Add(uniqueLigand);

        if (resName)   delete resName;
        if (resNumber) delete resNumber;

      }
      cumulCount = 0;
    }

  if (listResN)   delete listResN;
  if (lines)      delete lines;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
UniqueLigand* DistanceBindingSite::GetLargestLingand() {

  if (!this->location || !this->location->Length()) return NULL;
  if (!this->GetListLigandInComplex())              return NULL;

  if (!this->listLigands->Length())                 return NULL;

  int largest  = this->listLigands->Get(0)->amountAtoms;
  int position = 0;

  for (int i = 0; i < this->ligandFormul->Length(); i++) 
    if (this->listLigands->Get(i)->amountAtoms > largest) {
      largest  = this->listLigands->Get(i)->amountAtoms;
      position = i;
    }

  return this->listLigands->Get(position);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double DistanceBindingSite::DistancePoints(double x1, double y1, double z1, double x2, double y2, double z2) {
  return (sqrt(((x1 - x2)*(x1 - x2)) + ((y1 - y2)*(y1 - y2)) + ((z1 - z2)*(z1 - z2))));
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
